package com.fun.seckill.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fun.seckill.config.AccessLimit;
import com.fun.seckill.exception.GlobalException;
import com.fun.seckill.pojo.*;
import com.fun.seckill.rabbitmq.MQSender;
import com.fun.seckill.service.IGoodsService;
import com.fun.seckill.service.IOrderService;
import com.fun.seckill.service.ISeckillGoodsService;
import com.fun.seckill.service.ISeckillOrderService;
import com.fun.seckill.utils.JsonUtil;
import com.fun.seckill.vo.GoodsVo;
import com.fun.seckill.vo.RespBean;
import com.fun.seckill.vo.RespBeanEnum;
import com.wf.captcha.ArithmeticCaptcha;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Controller
@RequestMapping("/seckill")
@Slf4j
public class SecKillController implements InitializingBean {

    @Autowired
    private IGoodsService goodsService;

    @Autowired
    private ISeckillOrderService iSeckillOrderService;

    @Autowired
    private ISeckillGoodsService seckillGoodsService;

    @Autowired
    private IOrderService iOrderService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MQSender mqSender;

    private Map<Long,Boolean> EmptyStockMap = new HashMap<>();

    @Autowired
    private DefaultRedisScript<Long> redisScript;

    //测试接口，windos： 5000循环3次  qps： 3334

//    @PostMapping("/doSeckill")
//    @ResponseBody
//    public RespBean deSeckill(User user, long goodsId){
//        if(user==null)
//            return RespBean.error(RespBeanEnum.SESSION_ERROR);
//
//        //方法中定义了变量，如果goodsid没有库存了，map中会为true
//        if(EmptyStockMap.get(goodsId)){
//            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
//        }
//        //是否重复购买。
//        SeckillOrder  seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
//        if(seckillOrder!=null){
//            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
//        }
//
//        //判断秒杀商品库存是否充足
//        Long result = (Long) redisTemplate.execute(redisScript, Collections.singletonList("seckillGoods:" + goodsId), Collections.EMPTY_LIST);
//
//        if(result<0){
//            EmptyStockMap.put(goodsId,true);
//            redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
//            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
//        }
//
//        //通过mq发消息，异步下单
//        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
//        mqSender.sendSeckillMessage(JsonUtil.object2JsonStr(seckillMessage));
//        return RespBean.success(0);
//    }


    //秒杀功能，先判断reids中的库存是否足够，和是否是同一个用户下第二次订单，如果满足下单条件。
    //发送mq消息，通过mq消息队列异步下单。前端通过轮询，查询后台订单是否下单成功。
    @PostMapping("/{path}/doSeckill")
    @ResponseBody
    public RespBean deSeckill2(@PathVariable String path, User user, long goodsId){
        if(user==null)
            return RespBean.error(RespBeanEnum.SESSION_ERROR);

        //判断请求路径，路径存在redis中，1分钟时间限制
        boolean bool = iOrderService.checkPath(user, goodsId, path);
        if(!bool){
            return RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
        }
        //方法中定义了变量，如果goodsid没有库存了，map中会为true
        if(EmptyStockMap.get(goodsId)){
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }
        //是否重复购买。
        SeckillOrder  seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
        if(seckillOrder!=null){
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }

        //判断秒杀商品库存是否充足
        Long result = (Long) redisTemplate.execute(redisScript, Collections.singletonList("seckillGoods:" + goodsId), Collections.EMPTY_LIST);

        if(result<0){
            EmptyStockMap.put(goodsId,true);
            redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }

        //通过mq发消息，异步下单
        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
        mqSender.sendSeckillMessage(JsonUtil.object2JsonStr(seckillMessage));
        return RespBean.success(0);
    }

    //前端轮询获取秒杀状态
    //状态：   成功：orderId   失败：-1   排队中：0
    @GetMapping("getResult")
    @ResponseBody
    public RespBean getResult(User user,long goodsId){
        if(user == null)
        {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }
        long result = iSeckillOrderService.getResult(user, goodsId);
        return RespBean.success(result);
    }

    @GetMapping(value = "/captcha")
    public void verifyCode(User tUser, Long goodsId, HttpServletResponse response) {
        if (tUser == null || goodsId < 0) {
            throw new GlobalException(RespBeanEnum.REQUEST_ILLEGAL);
        }
        //设置请求头为输出图片的类型
        response.setContentType("image/jpg");
        response.setHeader("Pargam", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        //生成验证码
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 32, 3);
        redisTemplate.opsForValue().set("captcha:" + tUser.getId() + ":" + goodsId, captcha.text(), 300, TimeUnit.SECONDS);
        try {
            captcha.out(response.getOutputStream());
        } catch (IOException e) {
            log.error("验证码生成失败", e.getMessage());
        }
    }

    //得到秒杀请求路径，当访问的次数超过限制时，会拦截请求。
    @GetMapping(value = "/path")
    @ResponseBody
    @AccessLimit(second = 5,maxCount = 5)//限制5秒最大请求5次
    public RespBean getPath(User user, Long goodsId,String captcha, HttpServletRequest request) {
        if (user == null) {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }
        boolean check = iOrderService.checkCaptcha(user, goodsId, captcha);
        if (!check) {
            return RespBean.error(RespBeanEnum.ERROR_CAPTCHA);
        }
        String str = iOrderService.createPath(user, goodsId);
        return RespBean.success(str);
    }

    //bean初始化后，在本地预热库存，为false:有库存    true:没有库存
    @Override
    public void afterPropertiesSet() throws Exception {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        List<GoodsVo> goodsVo = goodsService.findGoodsVo();
        if (CollectionUtils.isEmpty(goodsVo)) {
            return;
        }
        goodsVo.forEach (goodsVo1 -> {
                valueOperations.set("seckillGoods:" + goodsVo1.getId(), goodsVo1.getStockCount());
                EmptyStockMap.put(goodsVo1.getId(),false);
            }
        );
    }

}
